Efter detta kapitel ska eleven känna till:
För att man ska kunna arbeta med filer behöver man definiera en ny struktur, d.v.s. en ny datatyp. Denna datatyp (struktur) finns redan deklarerad i stdio.h och den heter FILE. Tittar man i stdio.h finner man bland annat följande:
/* buffered I/O
macros */
#define
BUFSIZ 512
#define
_NFILE 40
#define EOF
(-1)
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
char
_flag;
char
_file;
};
typedef struct
_iobuf FILE;
Här har man skapat en ny typ kallad _iobuf, och sedan givit den ett nytt namn, FILE. Skapar man en egen variabel av denna typ kommer den att innehålla de fem variabler vi ser ovan. De funktioner som vi använder kommer att behöva dessa variabler.
Dessutom fick vi tre stycken textkonstanter. En av dem, EOF kommer vi att ha mycket nytta av. Anledningen till att EOF definieras så här är den att olika datorsystem kan ha olika värden för just EOF, men vi behöver inte veta vilket, här definieras den, så vi kan bara skriva EOF. Om du inte redan gissa det så står EOF för End Of File, d.v.s. filslut. Om du har gissat det så står det i alla fall för filslut. Man kan t.ex. testa i en läsloop på följande sätt:
while ((cTecken = getc(Infil)) != EOF)
Def finns många definitioner, varav vi får begränsa oss till att titta på några och se vad de kan ge oss. En del används endast av de funktioner vi använder.
/* fseek
constants */
#define
SEEK_CUR 1
#define
SEEK_END 2
#define
SEEK_SET 0
#define
FILENAME_MAX 128
#define
FOPEN_MAX 18
#define TMP_MAX
32767
#define
_SYS_OPEN 20
Därefter tittar vi på några av de funktioner vi har tillgång till (lugn bara, vi ska inte lära oss alla nu):
/* function
prototypes */
int __cdecl
fclose(FILE *);
int __cdecl
_fcloseall(void);
int __cdecl
feof(FILE *);
int __cdecl
ferror(FILE *);
int __cdecl
fgetc(FILE *);
int __cdecl
fgetpos(FILE *, fpos_t *);
char * __cdecl
fgets(char *, int, FILE *);
int __cdecl
_fileno(FILE *);
int __cdecl
_flushall(void);
FILE * __cdecl
fopen(const char *, const char *);
int __cdecl
fprintf(FILE *, const char *, ...);
int __cdecl
fputc(int, FILE *);
int __cdecl
fputs(const char *, FILE *);
size_t __cdecl
fread(void *, size_t, size_t, FILE *);
FILE * __cdecl
freopen(const char *, const char *, FILE *);
int __cdecl
fsetpos(FILE *, const fpos_t *);
int __cdecl
fseek(FILE *, long, int);
long __cdecl
ftell(FILE *);
size_t __cdecl
fwrite(const void *, size_t, size_t, FILE *);
int __cdecl
getc(FILE *);
int __cdecl
_getw(FILE *);
int __cdecl
remove(const char *);
int __cdecl
rename(const char *, const char *);
void __cdecl
rewind(FILE *);
int __cdecl
_rmtmp(void);
void __cdecl
setbuf(FILE *, char *);
int __cdecl
setvbuf(FILE *, char *, int, size_t);
char * __cdecl
_tempnam(char *, char *);
FILE * __cdecl
tmpfile(void);
char * __cdecl
tmpnam(char *);
int __cdecl
ungetc(int, FILE *);
int __cdecl
vfprintf(FILE *, const char *, va_list);
Vi kommer att titta närmre på en del av dessa funktioner nedan.
Man måste be operativsystemet att etablera kontakt med en fil, tala om vad man vill göra med den (läsa/skriva/förlänga), och tala om var i minnet data ska buffras etc. Operativsystemet kontrollerar även att man inte låter flera program samtidigt manipulerar samma fil. Därför är det viktigr att man meddelar operativsystemet när man är klar med den. Dessa två processer kallas att öppna repektive stänga filer.
Man öppnar en fil med följande syntax:
<FILE-variabel> = fopen(”<filnamn>”,”<arbetsläge>”);
FILE-variabel är alltså en pekarvariabel av typen FILE. När vi anropar fopen() kommer funktionen att ge oss en adress till filbuffer etc. som ingår i strukturen FILE. Om DOS av någon anledning inte kan öppna filen kommer vi att få värdet noll i pekaren. Detta måste kollas innan man försöker gå vidare.
Filnamn är ett filnamn enligt DOS-format.
Arbetsläge kan vara: r = read, läs fil.
w = write, skriv fil.
a = append, förläng fil.
Öppnar man en fil för att läsa kommer filpekaren att stå i början av filen. (Filpekaren gömmer sig i den strukturvariabel du deklarerat av typen FILE.) När du ber om nästa tecken kommer filpekaren att stegas framåt ett tecken i taget tills du hittar EOF.
Öppnar man en fil för att skriva kommer filpekaren också att stå i början av filen. När man sedan stänger filen kommer EOF att skrivas där filpekaren står. Då har man antingen skrivit över filens innehåll, eller flyttat EOF till filens början, och DOS kommer att säga att den är tom. Hur du än gör så förlorar du innehållet i filen. Finns inte filen kommer DOS att skapa den.
Vill du öppna en fil för skrivning utan att förlora innehållet ska du använda förlängning (append). Då sätts filpekaren vid EOF. Du kan då lägga till data i slutet av filen.
Exempel på filöppning:
infil = fopen(”c:\\katalog\\filnamn.txt”,”r”)
Syntaxen för att stänga en fil:
fclose(<FILE-variabel>);
Man behöver alltså bara ange pekaren, inte filnamnet, för att stänga filen.
Exempel:
fclose(infil);
Observera att fopen() levererade en adress i variabeln infil. Skulle DOS ha något att invända mot att vi öppnar filen får vi ingen adress, vi får värdet noll i stället. Det är alltså högst rekommendabelt att man testar pekaren innan man försöker läsa eller skriva:
if (infil == 0)
{
puts(”Det gick inte att öppna filen!”);
}
else
{
.
. // gör vad du ska göra med filen.
.
}
Man kan kombinera ihop funktionsanropet och testen (sånt här älskar äkta C-programmerare, koden blir ännu svårare att förstå):
if ((infil = fopen(”c:\\katalog\\filnamn.txt”,”r”)) == 0)
{
puts(”Det gick inte att öppna filen!”);
}
else
{
.
. // gör vad du ska göra med filen.
.
}
Och det du ska göra är naturligtvis att läsa eller skriva.
Ovanstående exempel handlar alla om sekvensfiler. En sekvensfil kan betraktas som en lång rad tecken som avslutats med ett filslutstecken (EOF, -1) vilka skrivits på en disk eller en diskett.
Vi kan läsa en fil m.h.a. funktionen fgetc(). Som du kan se ovan finns den deklarerad i stdio.h, och den vill ha en pekarvariabel av type FILE som parameter.
Exempel, öppna en fil och läs första bokstaven, visa bokstaven på skärmen och stäng filen:
#include
<stdio.h>
void main()
{
FILE *infil;
char cBokstav;
if((infil =
fopen("test.txt","r")) != 0)
{
cBokstav = fgetc(infil);
printf("Första bokstaven i filen är: %c. \n", cBokstav);
fclose(infil);
}
else puts("Det gick inte att öppna filen!");
}
Om då filen test.txt, vilken ligger i samma katalog som programmet, innehåller texten ”Hubba-hopp!”, kommer utskriften att bli:
Övningsuppgift:
·
Gör en textfil som heter test.txt m.h.a. Workbench editor, och
mata in texten: ”Denna fil har jag skrivit själv.
Nu ska jag göra ett program som kan läsa den och visa på skärmen.”
· Gör ett program lasfil som läser filen och skriver ut på skärmen.
· Utskriften ska se ut som nedan:
Övningsuppgift:
· Gör ett program som räknar antalet tecken i textfilen från exemplet ovan.
· Kalla programmet ccnt.
Överkursuppgift:
· Gör ett program som räknar orden i samma textfil.
· Kalla programmet wcnt.
Vill man skriva i en fil måste man öppna den med ‘w’ eller ‘a’ enligt ovan. Själva skrivningen kan man göra med funktionen fputc(). Funktionen vill ha en variabel av typen char eller int som första parameter, och pekaren till FILE-variabeln som andra parameter, t.ex:
FILE *utfil;
if ((utfil = fopen(”c:\\katalog\\filnamn.txt”,”w”)) == 0)
{
puts(”Det gick inte att öppna filen!”);
}
else
{
.
. // Skriv i filen.
.
}
Vill man t.ex. kopiera innehållet i en fil till en annan, tecken för tecken, kan man klara det med en enda liten loop:
while((cBokstav = fgetc(infil)) != EOF) fputc(cBokstav, utfil);
Övningsuppgift:
· Skriv ett program filecopy som kopierar filer enligt ovanstående.
· Först ska det fråga efter infilens namn, sedan efter utfilens.
· Därefter sker kopiering.
· Antal kopierade tecken ska slutligen redovisas.
· Efter avslutad körning bör QuickWin-fönstret se ut så här:
Man kan också använda fputs() och fgets() för att läsa skriva/läsa hela rader. Syntax:
fputs(<textpekare>, < FILE-variabel>);
fgets(<textpekare>, <charlistans längd - 1>, < FILE-variabel>);
Fgets() kopierar en rad från filen och lägger in där textpekaren pekar. Om raden är längre än maxlängd kommer läsningen att avbrytas där. Resten av raden läses i så fall vid nästa fgets(). Om filen är slut kommer fgets att returnera värdet noll, vilket man alltså bör testa vid varje läsning.
Överkursuppgift:
· Skriv ett program compare som jämför två filer.
· Använd radvis läsning.
· Jämför raderna med funktionen strcmp().
· Skriv en resultatfil som innehåller de rader som skiljer sig: första filens rad, andra filens rad och en blankrad (så att men ser vilka rader som hör ihop).
· Skapa textfiler att testa med. Kontrollera resultatet i resultatfilen.
Som nämnts tidigare har vi en filpekare som håller reda på var i filen vi läser/skriver, och den kan stå antingen i början eller i slutet av filen när vi öppnar den. Ibland kan det dock vara av intresse att snabbt ändra filpekaren till att läsa eller skriva på ett annat ställe i filen. Detta kan göras med funktionen fseek():
fseek(<FILE-variabel>, <avstånd>, <referenspunkt>)
< FILE-variabel> är den vanliga pekaren till din variabel av typen FILE, vilken fick sitt värde när du gjorde fopen().
<avstånd> är av typen long, och anger hur många bytes vi ska flytta filpekaren. Vill vi backa i filen är det tillåtet att använda negativa värden.
<referenspunkt> ska vara ett av tre värden, och talar om från vilken position avståndet räknas. (Man behöver alltså inte räkna från aktuell position):
0 = räkna från filens
början.
1 = räkna
från aktuell position.
2 = räkna
från filens slut.
Funktionen returnerar ett värde av typen int. Om man anger ett avstånd som i kombination med referenspunkten gör att man hamnar utanför filen, kommer returvärdet att vara -1, annars 0. Man bör därför använda fseek() i en test av något slag, t.ex:
if(fseek(infil,
avstand, 2) == 0)
{
// Läs...
}
Överkursuppgift:
· Skriv ett program krypto som krypterar en fil.
· Metoden ska vara den att varannat tecken ska läsas från slutet av infilen och varannat från början. Detta låter sig göras endast om filen innehåller ett jämnt antal tecken. om så inte är fallet lägger man tíll en blank i slutet av filen.
· Programmet skapar en ny fil.
· Användaren ska ange infil och utfil.
Överkursuppgift:
· Skriv ett program dekrypto som återställer en fil som skapats av krypto.
· Programmet skapar en ny fil.
· Användaren ska ange infil och utfil.